

import Foundation
import WCDBSwift


protocol DatabaseEntity: TableCodable {
    static var tableName: String { get }
}

extension DatabaseEntity {
    
    static var tableName: String {
        return "\(String(describing: Self.self))Table"
    }
    
    @discardableResult
    func insert() -> Bool {
        return DatabaseService.shared.insert(self)
    }
    
    @discardableResult
    func insertOrReplace() -> Bool {
        return DatabaseService.shared.insertOrReplace(self)
    }
    
    func asyncInsertOrReplace(completion: ((Bool) -> Void)? = nil) {
        GlobalQueue.dataBase.async {
            let res = DatabaseService.shared.insertOrReplace(self)
            completion?(res)
        }
    }
    
    @discardableResult
    static func insert(_ objects: [Self]) -> Bool {
        return DatabaseService.shared.insert(objects)
    }
    
    @discardableResult
    static func insertOrReplace(_ objects: [Self]) -> Bool {
        return DatabaseService.shared.insertOrReplace(objects)
    }
    
    static func getObject(where condition: Condition? = nil, orderBy orderList: [OrderBy]? = nil, offset: Offset? = nil) -> Self? {
        return DatabaseService.shared.getObject(Self.self, where: condition, orderBy: orderList, offset: offset)
    }

    static func getObjects(where condition: Condition? = nil, orderBy orderList: [OrderBy]? = nil, limit: Limit? = nil, offset: Offset? = nil) -> [Self]? {
        return DatabaseService.shared.getObjects(Self.self, where: condition, orderBy: orderList, limit: limit, offset: offset)
    }
    
    @discardableResult
    static func delete(where condition: Condition? = nil, orderBy orderList: [OrderBy]? = nil, limit: Limit? = nil, offset: Offset? = nil) -> Bool {
        return DatabaseService.shared.delete(Self.self, where: condition, orderBy: orderList, limit: limit, offset: offset)
    }
 
    // 页码，从 1 开始
    static func getObjects(where condition: Condition? = nil, orderBy: [OrderingTerm]? = nil, index: Int, limit: Int) -> [Self]? {
        DatabaseService.shared.getObjects(Self.self, where: condition, orderBy: orderBy, index: index, limit: limit)
    }
    
    func update(on propertyConvertibleList: PropertyConvertible..., where condition: Condition? = nil, orderBy orderList: [OrderBy]? = nil, limit: Limit? = nil, offset: Offset? = nil) -> Bool {
        DatabaseService.shared.update(Self.self, on: propertyConvertibleList, with: self, where: condition, orderBy: orderList, limit: limit, offset: offset)
        return true
    }
    
}

final class DatabaseService {
    
    static let shared = DatabaseService()
    private var tableCache: Set<String> = []  // 缓存已创建的表
    private(set) var database: Database = {
        let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
        return Database(at: path + "/database.db")
    }()
    
    private init() {}
    
    private func createTable<T: DatabaseEntity>(for type: T.Type) {
   
        let tableName = T.tableName
        
         if tableCache.contains(tableName) { return}
        
        do {
            let exists = try database.isTableExists(tableName)
            if !exists {
                try database.create(table: tableName, of: T.self)
            }
            tableCache.insert(tableName)
        } catch {
            print("Ensure table created error: \(error)")
        }
    }
    
    @discardableResult
    func insert<T: DatabaseEntity>(_ objects: [T]) -> Bool {
        guard !objects.isEmpty else { return false}
        
        createTable(for: T.self)
        
        do {
            try database.insert(objects, intoTable: T.tableName)
            return true
        } catch {
//            print("insert error: \(error)")
            return false
        }
    }
    
    @discardableResult
    func insertOrReplace<T: DatabaseEntity>(_ objects: [T]) -> Bool{
        guard !objects.isEmpty else { return false}
        
        createTable(for: T.self)
        do {
            try database.insertOrReplace(objects, intoTable: T.tableName)
            return true
        } catch {
//            print("insertOrReplace error: \(error)")
            return false
        }
    }
    
    @discardableResult
    func insert<T: DatabaseEntity>(_ objects: T...) -> Bool {
        guard !objects.isEmpty else { return false}
        
        createTable(for: T.self)
        do {
            try database.insert(objects, intoTable: T.tableName)
            return true
        } catch {
//            print("insert error: \(error)")
            return false
        }
    }
    
    
    @discardableResult
    func insertOrReplace<T: DatabaseEntity>(_ objects: T...) -> Bool {
        guard !objects.isEmpty else { return false}
        
        createTable(for: T.self)
        do {
            try database.insertOrReplace(objects, intoTable: T.tableName)
            return true
        } catch {
//            print("insertOrReplace error: \(error)")
            return false
        }
    }
    
    func getObjects<T: DatabaseEntity>(
        _ type: T.Type,
        where condition: Condition? = nil,
        orderBy: [OrderingTerm]? = nil,
        index: Int, // 页码，从 1 开始
        limit: Int  // 每页条数
    ) -> [T]? {
        createTable(for: T.self)
        let offset = max(index - 1, 0) * limit
        do {
            return try database.getObjects(
                fromTable: T.tableName,
                where: condition,
                orderBy: orderBy,
                limit: limit,
                offset: offset
            )
        } catch {
            print("FetchPage error: \(error)")
            return nil
        }
    }
    
    @discardableResult
    func delete<T: DatabaseEntity>(_ type: T.Type,
                             where condition: Condition? = nil,
                             orderBy orderList: [OrderBy]? = nil,
                             limit: Limit? = nil,
                             offset: Offset? = nil) -> Bool {
        createTable(for: T.self)
        do {
            try database.delete(fromTable: T.tableName, where: condition, orderBy: orderList, limit: limit, offset: offset)
            return true
        } catch {
            print("Delete error: \(error)")
            return false
        }
    }
    
    func getObjects<T: DatabaseEntity>(_ type: T.Type,
                                 where condition: Condition? = nil,
                                 orderBy orderList: [OrderBy]? = nil,
                                 limit: Limit? = nil,
                                 offset: Offset? = nil) -> [T]? {
        createTable(for: T.self)
        do {
            return try database.getObjects(fromTable: T.tableName, where: condition, orderBy: orderList, limit: limit, offset: offset)
        } catch {
            print("getObjects error: \(error)")
            return nil
        }
    }
    
    func getObject<T: DatabaseEntity>(_ type: T.Type,
                                where condition: Condition? = nil,
                                orderBy orderList: [OrderBy]? = nil,
                                offset: Offset? = nil) -> T? {
        createTable(for: T.self)
        
        do {
            return try database.getObject(fromTable: T.tableName, where: condition, orderBy: orderList)
        } catch  {
            print("getObject error: \(error)")
            return nil
        }
        
    }
    
    @discardableResult
    func update<T: DatabaseEntity>(_ type: T.Type,
                             on propertyConvertibleList: [PropertyConvertible],
                             with object: T,
                             where condition: Condition? = nil,
                             orderBy orderList: [OrderBy]? = nil,
                             limit: Limit? = nil,
                             offset: Offset? = nil) -> Bool {
        createTable(for: T.self)
        
        do {
            try database.update(table: T.tableName, on: propertyConvertibleList, with: object, where: condition, orderBy: orderList, limit: limit, offset: offset)
            return true
        } catch  {
            print("update error: \(error)")
            return false
        }
    }

    
}



